package model.micro
{
	import control.micro.FieldUnitController;
	import model.macro.Unit;
	import model.macro.Weapon;
	import model.micro.fieldunitabilities.Charge;
	import model.micro.fieldunitabilities.FieldUnitAbility;
	import model.micro.projectiles.Projectile;
	import util.Settings;
	
	/**
	 * A unit in a battle that has been deployed to the field.
	 */
	public class FieldUnit extends Attackable
	{
		static private const HIT_MULTIPLIER:Number = 0.001;
		
		/** Unit this FieldUnit represents */
		protected var unit:Unit;
		/** The battle this unit is in */
		protected var battle:Battle;
		/** Percent damage reduction against attacks */
		private var armor_value:Number;
		/** Flat damage reduction against attacks */
		private var damage_reduction:int;
		/** Number of "length units" this unit moves each second */
		private var move_speed:int;
		/** The effects currently affecting this unit. */
		private var active_effects:Vector.<FieldUnitEffect>;
		/** The abilities this unit can activate. */
		public var abilities:Vector.<FieldUnitAbility>;
		
		public var controller:FieldUnitController;
		
		/**
		 * Create a new field unit for the given macro unit at a specific
		 * location on the battlefield.
		 * @param	unit	macro unit to represent
		 * @param	x		field location
		 */
		public function FieldUnit(unit:Unit, x:int, battle:Battle)
		{
			super(x, battle.ground.getBoundingBox().top, Settings.METER * 3, Settings.METER * 2);
			this.battle = battle;
			this.unit = unit;
			armor_value = unit.armor.armorMultiplier;
			move_speed = Settings.METER * 1.2;
			morale = 1.0;
			active_effects = new Vector.<FieldUnitEffect>();
			abilities = new Vector.<FieldUnitAbility>;
			abilities.push(new Charge(this));
		}
		
		/**
		 * Update by one frame
		 * @param	battle
		 */
		public function update():void
		{
			bounding_box.bottom = battle.ground.getBoundingBox().top;
			
			for (var i:int = active_effects.length - 1; i >= 0; i--)
			{
				active_effects[i].update();
				if (active_effects[i].readyToRemove())
				{
					active_effects.splice(i, 1);
				}
			}
		}
		
		/**
		 * Move the unit with a given percent of its movespeed.
		 * @param	speed	must be in range [-1,1]
		 */
		public function move(speed:Number):void
		{
			var timestep:Number = Settings.getTimeStep();
			speed = speed * move_speed * timestep;
			for each (var effect:FieldUnitEffect in active_effects)
			{
				speed = effect.processSpeed(speed);
			}
			bounding_box.move(speed, 0);
		}
		
		/**
		 * Activates the ability with the specific index.
		 * @param	abilityIndex
		 */
		public function activateAbility(ability_index:int):void
		{
			if (ability_index >= 0 && ability_index < abilities.length)
			{
				abilities[ability_index].activate();
			}
			else
			{
				trace("invalid ability: " + ability_index);
			}
		}
		
		/**
		 * Checks if an ability is active.
		 * @param	ability_index
		 * @return	true if the ability is active
		 */
		public function checkActiveAbility(ability_index:int):Boolean
		{
			if (ability_index >= 0 && ability_index < abilities.length)
			{
				return abilities[ability_index].isActive();
			}
			else
			{
				trace("invalid ability: " + ability_index);
				return false;
			}
		}
		
		/**
		 * Deactivates the ability with the specific index.
		 * @param	abilityIndex
		 */
		public function deactivateAbility(ability_index:int):void
		{
			if (ability_index >= 0 && ability_index < abilities.length)
			{
				abilities[ability_index].deactivate();
			}
			else
			{
				trace("invalid ability: " + ability_index);
			}
		}
		
		/**
		 * Attacke a target.
		 * @param	target
		 */
		public function attack(target:Attackable):void
		{
			if (readyToRemove())
			{
				throw new Error("dead unit attacking");
			}
			target.attacked(damage * Settings.getTimeStep());
		}
		
		override public function onHit(projectile:Projectile):void
		{
			super.onHit(projectile);
			
			var damage:Number = armor_value * (projectile.getDamage() - (damage_reduction)) * HIT_MULTIPLIER;
			
			if (Math.random() < damage)
			{
				unit.number_of_soldiers--;
			}
		}
		
		override public function attacked(damage:Number):void
		{
			damage = armor_value * (damage - damage_reduction * unit.number_of_soldiers * Settings.getTimeStep()) * HIT_MULTIPLIER;
			unit.number_of_soldiers -= int(damage);
			if (Math.random() < damage % 1.0)
			{
				unit.number_of_soldiers--;
			}
		
			// morale calculation
		}
		
		/**
		 * @return	the amount of damage per second.
		 */
		public function get damage():int
		{
			var result:Number = unit.weapon.damage * unit.number_of_soldiers * morale;
			for each (var effect:FieldUnitEffect in active_effects)
			{
				result = effect.process_damage(result);
			}
			return result;
		}
		
		/**
		 * Returns the macro unit this field unit represents.
		 * @return
		 */
		public function get macroUnit():Unit
		{
			return unit;
		}
		
		/**
		 * @return	the number of soldiers in this unit
		 */
		public function get numberSoldiers():int
		{
			return unit.number_of_soldiers;
		}
		
		/**
		 * @return	the movement speed of the unit.
		 */
		public function get moveSpeed():Number
		{
			return move_speed;
		}
		
		/**
		 * @return	the current weapon
		 */
		public function get weapon():Weapon
		{
			return unit.weapon;
		}
		
		/**
		 * @return	true if this unit should be deleted
		 */
		public function readyToRemove():Boolean
		{
			return unit.number_of_soldiers <= 0;
		}
		
		/**
		 * remove the unit from the game
		 * 		wait, from the battle or from the macro?
		 */
		public function destroy():void
		{
			
		}
		
		public function get activeEffects():Vector.<FieldUnitEffect>
		{
			return active_effects;
		}
		
		public function get morale():Number
		{
			return unit.morale;
		}
		
		public function set morale(value:Number):void
		{
			unit.morale = value;
		}
	}

}